// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Abilities/LyraGameplayAbility.h"
#include "AbilitySystemComponent.h"
#include "NativeGameplayTags.h"

#include "LyraAbilitySystemComponent.generated.h"

class AActor;
class UGameplayAbility;
/** Mapping of how ability tags block or cancel other abilities */

/** 能力标签阻塞或取消其他能力的映射。 */
class ULyraAbilityTagRelationshipMapping;
class UObject;
//
// Information about script execution at one stack level.
//

// 关于脚本执行信息在某堆栈层。
struct FFrame;

/**
 *	Handle for Targeting Data. This servers two main purposes:
 *		-Avoid us having to copy around the full targeting data structure in Blueprints
 *		-Allows us to leverage polymorphism in the target data structure
 *		-Allows us to implement NetSerialize and replicate by value between clients/server
 *
 *		-Avoid using UObjects could be used to give us polymorphism and by reference passing in blueprints.
 *		-However we would still be screwed when it came to replication
 *
 *		-Replication by value
 *		-Pass by reference in blueprints
 *		-Polymophism in TargetData structure
 */

/**
 * FGameplayAbilityTargetDataHandle: 
 * 节点数据句柄。这服务有两个主要目的：
 *	-避免我们不得不在蓝图中去复制完全节点数据结构体。
 *	-允许在节点数据结构体中借助多态式。
 *	-允许我们去实现NetSerialize和复制通过值在客户端和服务器之间。
 *	
 *	-避免使用UObject对象被用于给定多态式和在蓝图中被引用。
 *	-然而，当涉及到复制方面时，我们仍然会陷入困境。  
 *  -通过值复制。
 *  -通过蓝图引用。
 *  -多态式节点数据。
 */
struct FGameplayAbilityTargetDataHandle;

LYRAGAME_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Gameplay_AbilityInputBlocked);

/**
 * ULyraAbilitySystemComponent
 *
 *	Base ability system component class used by this project.
 */

/**
 * 用于这个项目的基本ASC类。
 */
UCLASS()
class LYRAGAME_API ULyraAbilitySystemComponent : public UAbilitySystemComponent
{
	GENERATED_BODY()

public:

	ULyraAbilitySystemComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

	//~UActorComponent interface

	/**
	 * Ends gameplay for this component.
	 * Called from AActor::EndPlay only if bHasBegunPlay is true
	 */

	/**
	 * EndPlay: 
	 * 针对此组件结束游戏玩法。
	 * 仅bHasBegunPlay为True时，从AActor::EndPlay回调。
	 */
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
	//~End of UActorComponent interface

	/**
	 *	Initialized the Abilities' ActorInfo - the structure that holds information about who we are acting on and who controls us.
	 *      OwnerActor is the actor that logically owns this component.
	 *		AvatarActor is what physical actor in the world we are acting on. Usually a Pawn but it could be a Tower, Building, Turret, etc, may be the same as Owner
	 */

	/**
	 * InitAbilityActorInfo: 
	 * 初始化能力角色信息 - 这个结构体拥有关于我们正在操控的对象和谁控制我们的信息。
	 *  -OwnerActor是逻辑拥有这个组件的Actor。
	 *  -AvatarActor是物理Actor在世界中我们正在操控的。通常是一个Pawn，但它也可能是一座Tower、Building、Turret等等，可能与所有者是同一个。
	 */
	virtual void InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor) override;
	/**
	 * TFunctionRef<FuncType>
	 *
	 * A class which represents a reference to something callable.  The important part here is *reference* - if
	 * you bind it to a lambda and the lambda goes out of scope, you will be left with an invalid reference.
	 *
	 * FuncType represents a function type and so TFunctionRef should be defined as follows:
	 *
	 * // A function taking a string and float and returning int32.  Parameter names are optional.
	 * TFunctionRef<int32 (const FString& Name, float Scale)>
	 *
	 * If you also want to take ownership of the callable thing, e.g. you want to return a lambda from a
	 * function, you should use TFunction.  TFunctionRef does not concern itself with ownership because it's
	 * intended to be FAST.
	 *
	 * TFunctionRef is most useful when you want to parameterize a function with some caller-defined code
	 * without making it a template.
	 *
	 * Example:
	 *
	 * // Something.h
	 * void DoSomethingWithConvertingStringsToInts(TFunctionRef<int32 (const FString& Str)> Convert);
	 *
	 * // Something.cpp
	 * void DoSomethingWithConvertingStringsToInts(TFunctionRef<int32 (const FString& Str)> Convert)
	 * {
	 *     for (const FString& Str : SomeBunchOfStrings)
	 *     {
	 *         int32 Int = Convert(Str);
	 *         DoSomething(Int);
	 *     }
	 * }
	 *
	 * // SomewhereElse.cpp
	 * #include "Something.h"
	 *
	 * void Func()
	 * {
	 *     // First do something using string length
	 *     DoSomethingWithConvertingStringsToInts([](const FString& Str) {
	 *         return Str.Len();
	 *     });
	 *
	 *     // Then do something using string conversion
	 *     DoSomethingWithConvertingStringsToInts([](const FString& Str) {
	 *         int32 Result;
	 *         TTypeFromString<int32>::FromString(Result, *Str);
	 *         return Result;
	 *     });
	 * }
	 */

	/**
	 * TFunctionRef<FuncType>: 
	 * 代表可回调的某事的引用的类。这重要的部分是引用 - 如果你将它绑定到Lambda表达式并且Lambda超出了作用域，你将会得到一个无效的引用。
	 * NOTE: 其实就是定义个函数指针引用。
	 */
	typedef TFunctionRef<bool(const ULyraGameplayAbility* LyraAbility, FGameplayAbilitySpecHandle Handle)> TShouldCancelAbilityFunc;
	void CancelAbilitiesByFunc(TShouldCancelAbilityFunc ShouldCancelFunc, bool bReplicateCancelAbility);

	void CancelInputActivatedAbilities(bool bReplicateCancelAbility);

	void AbilityInputTagPressed(const FGameplayTag& InputTag);
	void AbilityInputTagReleased(const FGameplayTag& InputTag);

	void ProcessAbilityInput(float DeltaTime, bool bGamePaused);
	void ClearAbilityInput();

	bool IsActivationGroupBlocked(ELyraAbilityActivationGroup Group) const;
	void AddAbilityToActivationGroup(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* LyraAbility);
	void RemoveAbilityFromActivationGroup(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* LyraAbility);
	void CancelActivationGroupAbilities(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* IgnoreLyraAbility, bool bReplicateCancelAbility);

	// Uses a gameplay effect to add the specified dynamic granted tag.

	// 使用游戏玩法效果去添加具体动态赋予标签。
	void AddDynamicTagGameplayEffect(const FGameplayTag& Tag);

	// Removes all active instances of the gameplay effect that was used to add the specified dynamic granted tag.

	// 移除所有的激活的被用于去添加具体动态赋予标签的游戏效果实例。
	void RemoveDynamicTagGameplayEffect(const FGameplayTag& Tag);

	/** Gets the ability target data associated with the given ability handle and activation info */

	/** 获取关联给定能力句柄和激活信息的能力节点数据。 */
	void GetAbilityTargetData(const FGameplayAbilitySpecHandle AbilityHandle, FGameplayAbilityActivationInfo ActivationInfo, FGameplayAbilityTargetDataHandle& OutTargetDataHandle);

	/** Sets the current tag relationship mapping, if null it will clear it out */

	/** 设置当前节点关联映射，如果为空则清空它。 */
	void SetTagRelationshipMapping(ULyraAbilityTagRelationshipMapping* NewMapping);
	
	/** Looks at ability tags and gathers additional required and blocking tags */

	/** 寻找能力标签并且收集添加的请求的和阻塞的标签。 */
	void GetAdditionalActivationTagRequirements(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer& OutActivationRequired, FGameplayTagContainer& OutActivationBlocked) const;

protected:

	void TryActivateAbilitiesOnSpawn();

	virtual void AbilitySpecInputPressed(FGameplayAbilitySpec& Spec) override;
	virtual void AbilitySpecInputReleased(FGameplayAbilitySpec& Spec) override;

	virtual void NotifyAbilityActivated(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability) override;
	virtual void NotifyAbilityFailed(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason) override;
	virtual void NotifyAbilityEnded(FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, bool bWasCancelled) override;
	virtual void ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bEnableBlockTags, const FGameplayTagContainer& BlockTags, bool bExecuteCancelTags, const FGameplayTagContainer& CancelTags) override;
	virtual void HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bCanBeCanceled) override;

	/** Notify client that an ability failed to activate */

	/** 通知客户端一个能力激活失败。 */
	UFUNCTION(Client, Unreliable)
	void ClientNotifyAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason);

	void HandleAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason);
protected:

	// If set, this table is used to look up tag relationships for activate and cancel

	// 如果进行了设置，那么此表将用于查找激活和取消操作的标签关系。
	UPROPERTY()
	TObjectPtr<ULyraAbilityTagRelationshipMapping> TagRelationshipMapping;

	// Handles to abilities that had their input pressed this frame.

	// 能力已经它们输入按下这帧的句柄。
	TArray<FGameplayAbilitySpecHandle> InputPressedSpecHandles;

	// Handles to abilities that had their input released this frame.

	// 能力已经它们输入抬起这帧的句柄。
	TArray<FGameplayAbilitySpecHandle> InputReleasedSpecHandles;

	// Handles to abilities that have their input held.

	// 能力已经它们输入持续的句柄。
	TArray<FGameplayAbilitySpecHandle> InputHeldSpecHandles;

	// Number of abilities running in each activation group.

	/**
	 * 在每个激活能力组中运行的能力数量。
	 * NOTE: 学到了这么定义数组的方式，利用枚举定义。
	 */
	int32 ActivationGroupCounts[(uint8)ELyraAbilityActivationGroup::MAX];
};
